package com.org.platform.support.conponents.limit;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import javax.validation.constraints.NotNull;

/**
 *    拦截器使用 demo
 *    @Override
 *    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{
 *      String servletPath = request.getServletPath();
 *      Integer limitCount = 3;
 *      Long limitTime = 3 * 1000L; // 这个请求,3秒内范围3次
 *      boolean limitResult = Limit.checkLimit(servletPath,limitTime,limitCount);
 *      System.out.println("isLimit:" + limitResult);
 *      return limitResult;
 *    }
 */

public class Limit {

  private static final Map<String, Limit> reqTimesMap = new ConcurrentHashMap<>();

  private Long lastLimitTime;

  private final AtomicInteger reqNumber = new AtomicInteger(0);

  private Limit(@NotNull Long lastLimitTime) {
    this.lastLimitTime = lastLimitTime;
  }

  private Limit(@NotNull Long lastLimitTime, int reqNumber) {
    this.lastLimitTime = lastLimitTime;
    this.reqNumber.set(reqNumber);
  }

  public int getCurrentNum() {
    return reqNumber.incrementAndGet();
  }

  public Long getLastLimitTime() {
    return lastLimitTime;
  }

  /**
   * @param reqUrl 请求url
   * @param reqTime 请求时间,传当前时间
   * @param limitTime 限制时间 比如3秒,传 3000L
   * @param limitCount 限制次数 3
   * @return
   */
  private boolean checkLimit(String reqUrl, Long reqTime, Long limitTime, Integer limitCount) {
    int currentNum = this.getCurrentNum();
    Long lastLimitTime = this.getLastLimitTime();
    if (reqTime - lastLimitTime > limitTime) {
      // 如果 请求时间减去初始访问时间大于限制时间,即超时,返回true
      Limit limit = new Limit(reqTime, 1);
      Limit.reqTimesMap.put(reqUrl, limit);
      return true;
    } else {
      Limit.reqTimesMap.put(reqUrl, this);
      if (currentNum > limitCount) {
        // 判断当前次数大于 限制次数
        return false;
      } else {
        return true;
      }
    }
  }

  /**
   * @param reqUrl 请求url
   * @param limitTime 限制时间 比如3秒,传 3000L
   * @param limitCount 限制次数 3
   * @return
   */
  public static boolean canAccess(String reqUrl, Long limitTime, Integer limitCount) {
    Long nowTime = System.currentTimeMillis();
    Limit limit = new Limit(nowTime);
    Limit lastLimit = Limit.reqTimesMap.putIfAbsent(reqUrl, limit);
    if (null == lastLimit) {
      lastLimit = limit;
    }
    return lastLimit.checkLimit(reqUrl, nowTime, limitTime, limitCount);
  }

}
